import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
import 'dart:collection';
import 'package:dio/dio.dart';
import 'package:dio/adapter.dart';
import 'package:protobuf/protobuf.dart';
import 'custom_dio_logger.dart';
import 'rpc.pb.dart';
import 'simple_log.dart';
export 'pb_function.dart';

/// 网络请求通用配置
class FunctionConfig {
  String host = "https://beta.2tianxin.com:443/proxymsg";
  String serviceHost = "huoxing";
  String appid = "20";
  String token;
  String env = "beta";
  String channel = "official";
  String telecomoper = "CMCC";
  String version = "1.0.0-SNAPSHOT";
  String deviceid = "X6ja9tEQ4XYDAPUWP0pKv58B";
  String client = "android;29;Xiaomi:M2002J9E;1.0.0-SNAPSHOT;1;";
  String application = "dycore";
}

/// 基础配置
FunctionConfig functionConfig = FunctionConfig();

Dio _dio;

void getDeviceInfo() async {
//  DeviceInfoPlugin deviceInfo = new DeviceInfoPlugin();
//  if (Platform.isIOS) {
//    IosDeviceInfo iosDeviceInfo = await deviceInfo.iosInfo;
////    functionConfig.deviceid = iosDeviceInfo.
//    functionConfig.version = iosDeviceInfo.systemVersion;
//  } else if (Platform.isAndroid) {
//    //stringBuilder.append(VERSION.SDK_INT + ";");
//    //                        stringBuilder.append(Build.BRAND + ":" + Build.MODEL + ";");
//    //                        stringBuilder.append(versionName() + ";");
//    //                        stringBuilder.append(versionCode() + ";");
//    //                        if (EmulatorCheckUtil.getSingleInstance().isEmulator(gContext)) {
//    //                            if (TextUtils.isEmpty(EmulatorCheckUtil.getSingleInstance().getEmulatorType())) {
//    //                                stringBuilder.append("emulatorType : others;");
//    //                            } else {
//    //                                stringBuilder.append("emulatorType : " + EmulatorCheckUtil.getSingleInstance().getEmulatorType() + ";");
//    //                            }
//    //                        }
//    AndroidDeviceInfo androidDeviceInfo = await deviceInfo.androidInfo;
////    functionConfig.version = androidDeviceInfo.version.codename;
//  }
}

Map<String, dynamic> _getDefaultHeader() {
  var headers = new HashMap<String, dynamic>();
  headers["X-Token"] = functionConfig.token;
  headers["client"] = functionConfig.client;
  headers["telecomoper"] = "CMCC";
  headers["version"] = functionConfig.version;
  headers["deviceid"] = functionConfig.deviceid;
  headers["channel"] = functionConfig.channel;
  headers["application"] = functionConfig.application;
  headers["no_auth_id"] = "0";
  headers["env"] = functionConfig.env;
  headers["appid"] = functionConfig.appid;
  headers["X-pro-tm"] = "0";
  headers["X-rec-tm"] = "0";
  return headers;
}

HashMap<String, String> _opt = new HashMap();

Dio _instance() {
  if (_dio == null) {
    getDeviceInfo();
    Map<String, dynamic> params = new Map();
    Map<String, dynamic> headers = _getDefaultHeader();
    params["Connection"] = "close";
    params.addAll(headers);
    BaseOptions option = new BaseOptions(
        connectTimeout: 50 * 1000,
        receiveTimeout: 50 * 1000,
        headers: params,
        contentType: 'multipart/form-data',
        responseType: ResponseType.bytes);
    _dio = new Dio(option);
    _dio.interceptors.add(new CustomDioLogger(requestHeader: false));
    (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) {
      client.badCertificateCallback = (cert, host, port) {
        return true;
      };
    };
  }
  return _dio;
}

class HttpModel<Res> {
  int code;
  String msg;
  Res data;

  bool isError() {
    return !isSuccess();
  }

  bool isSuccess() {
    return code == 0;
  }

  HttpModel.gen(int code, String msg, Res data) {
    this.code = code;
    this.msg = msg;
    this.data = data;
    LogUtil.d("-----------------------------------------------------------------");
  }
}

setServiceHost(newHost) {
  functionConfig.serviceHost = newHost;
}

setAppId(appId) {
  functionConfig.appid = appId;
  _instance().options.headers["appid"] = appId;
}

setEnv(env) {
  functionConfig.env = env;
  _instance().options.headers["env"] = env;
}

setToken(token) {
  functionConfig.token = token;
  _instance().options.headers["X-Token"] = token;
}

Future<HttpModel<Rsp>> executeNet<Req extends GeneratedMessage, Rsp extends GeneratedMessage>(
    Req req, Rsp rsp, module, func,
    {serviceHost, env, bool errorToast = true}) async {
  var dio = _instance();
  var input = RPCInput();
  input.obj = "${serviceHost ?? functionConfig.serviceHost}.$module";
  input.func = func;
  input.req = req.writeToBuffer();

  var headers = new HashMap<String, dynamic>();
  headers.addAll(dio.options.headers);
  if (env != null) {
    headers["env"] = env;
  }
  LogUtil.d("----------------------------Request Start--------------------------");
  LogUtil.d("serverName:${input.obj} func:${input.func}  params:\n$req");
  LogUtil.d("-------------------------------------------------------------------");
  _opt["appid"] = "20";
  input.opt.clear();
  input.opt.addAll(_opt);
  var body = ByteStream.fromBytes(input.writeToBuffer());
  Response response =
      await dio.post(functionConfig.host, data: body, options: Options(headers: headers));
  try {
    LogUtil.d("----------------------------Request End---------------------------");
    LogUtil.d("module:${input.obj} func:${input.func} ");
    if (response != null && response.data != null) {
      var rpcOutput = RPCOutput.fromBuffer(response.data);
      if (rpcOutput.ret == 0) {
        rsp.mergeFromBuffer(rpcOutput.rsp);
        LogUtil.d("result [[\n$rsp\n]]");
        return HttpModel.gen(rpcOutput.ret, response.statusMessage, rsp);
      }
      LogUtil.d("code:${rpcOutput.ret} message:${rpcOutput.desc} \n");
      if (errorToast) {
//        showToast(rpcOutput.desc);
      }
      return HttpModel.gen(rpcOutput.ret, rpcOutput.desc, null);
    } else {
      return HttpModel.gen(-1, "请求异常(-1)", null);
    }
  } catch (e) {
    return HttpModel.gen(-1, "请求异常(-1)", null);
  }
}

/// 采用class的方式创建function
//abstract class BaseFunction<Req extends GeneratedMessage, Rsp extends GeneratedMessage> {
//  final Req req;
//
//  BaseFunction(this.req);
//
//  String getModule();
//
//  String getFuncName();
//
//  Rsp newProxy();
//
//  Future<HttpModel<Rsp>> execute() async {
//    return executeNet(req, newProxy(), getModule(), getFuncName());
//  }
//}

/// A stream of chunks of bytes representing a single piece of data.
class ByteStream extends StreamView<List<int>> {
  ByteStream(Stream<List<int>> stream) : super(stream);

  factory ByteStream.fromBytes(List<int> bytes) => ByteStream(Stream.fromIterable([bytes]));

  Future<Uint8List> toBytes() {
    var completer = Completer<Uint8List>();
    var sink =
        ByteConversionSink.withCallback((bytes) => completer.complete(Uint8List.fromList(bytes)));
    listen(sink.add, onError: completer.completeError, onDone: sink.close, cancelOnError: true);
    return completer.future;
  }

  Future<String> bytesToString([Encoding encoding = utf8]) => encoding.decodeStream(this);

  Stream<String> toStringStream([Encoding encoding = utf8]) => encoding.decoder.bind(this);
}
